package FOM; import java.io.File; import edu.stanford.nlp.ling.Sentence; import edu.stanford.nlp.ling.HasWord; import edu.stanford.nlp.ling.TaggedWord; import edu.stanford.nlp.tagger.maxent.MaxentTagger; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.semanticweb.owlapi.apibinding.OWLManager; import org.semanticweb.owlapi.model.AddAxiom; import org.semanticweb.owlapi.model.IRI; import org.semanticweb.owlapi.model.OWLAnnotation; import org.semanticweb.owlapi.model.OWLAnnotationProperty; import org.semanticweb.owlapi.model.OWLAxiom; import org.semanticweb.owlapi.model.OWLClass; import org.semanticweb.owlapi.model.OWLDataFactory; import org.semanticweb.owlapi.model.OWLLiteral; import org.semanticweb.owlapi.model.OWLOntology; import org.semanticweb.owlapi.model.OWLOntologyCreationException; import org.semanticweb.owlapi.model.OWLOntologyManager; import org.semanticweb.owlapi.model.OWLOntologyStorageException; import FOM.StopWords; import edu.stanford.nlp.process.Morphology; import edu.stanford.nlp.ling.WordTag; import CFM.CorrelationFunction; public class FuzzyOntologyMiner { /** * Metodo che restituisce la matrice di frequenze dei termini nella collezione di documenti * @param path Path della cartella contenente i documenti da cui estrarre l'ontologia * @param lower Soglia minima di frequenza, al di sotto della quale bisogna scartare i termini * @param upper Soglia massima di frequenza, al di sopra della quale bisogna scartare i termini * @param windowSize Numero di termini di cui deve essere termosta la finestra scorrevole * @param searchPattern Array di Tag indicanti il pattern di termini su cui ci si vuole focalizzare: * non vi è controllo sulla correttezza di questi tag, si da per scontato che siano correttamente inseriti * e siano nella forma "NN","NNS","ADJ" * @return Un oggetto di tipo TermFrequencies<String> contenente le frequenze mutue e assolute dei termini. * @throws IllegalArgumentException Se il numero di pattern è maggiore della grandezza della finestra scorrevole, * o se il path non è una directory. * @throws ClassNotFoundException * @throws IOException */ public static TermFrequencies<String> extractFrequencies( String path, int windowSize, String[] searchPattern) throws IllegalArgumentException, ClassNotFoundException, IOException{ if( searchPattern.length > windowSize) throw new IllegalArgumentException("Il numero di tag non può essere maggiore della grandezza della finestra."); TermFrequencies<String> freq = new TermFrequencies<String>(); File dir = null; File[] listOfFiles; dir = new File(path); if(!dir.isDirectory()) throw new IllegalArgumentException(path + " is not a directory."); StopWords sw = new StopWords(); listOfFiles = dir.listFiles(); for(File file: listOfFiles){ if(file.isFile()){ MaxentTagger tagger = null; List<List<HasWord>> sentences = null; ArrayList<TaggedWord> tSentence = new ArrayList<TaggedWord>(); Morphology stemmer = new Morphology(); TaggedWord curr; String term; tagger = new MaxentTagger("left3words-wsj-0-18.tagger"); sentences = MaxentTagger.tokenizeText(new BufferedReader(new FileReader(file))); /* * Lavoro su ogni frase separatamente */ for(List<HasWord> sentence: sentences){ // si effettua il POS tagging sulla frase tSentence = tagger.tagSentence(sentence); /* filtered sentence conterrà parole taggate escluse stop words * e punteggiatura su cui è già stato effettuato lo stemming */ ArrayList<TaggedWord> filteredSentence = new ArrayList<TaggedWord>(); // dalla frase taggata bisogna eliminare stop words ed effettuare lo stemming for(TaggedWord word: tSentence){ /* la parola viene portata in lowercase ed è soggetta a stemming. * La parola ottenuta viene aggiunta ai termini filtrati solo se non è una stopword * e non è un segno di punteggiatura (ovvero rispetta la regexp [a-zA-Z0-9]+) */ word.setWord(word.word().toLowerCase()); word.setWord(stemmer.lemma(word.word(), word.tag())); if(!sw.isStopWord(word.word()) && word.word().matches("[a-zA-Z]+")){ //\\w+")){ filteredSentence.add(word); } } /* * Per ogni finestra di testo di largheza windowSize, bisogna controllare * se i tag delle parole rispettano il pattern di ricerca specificato come * parametro della classe, nel qual caso si memorizzano le relative frequenze. */ for(int i = 0; i < filteredSentence.size() - windowSize + 1; i++ ){ // Array contenente i termini già visti in questa finestra ArrayList<String> alreadySeen = new ArrayList<String>(); freq.augmentWindows(); // Per ogni termine nella finestra for(int j = i; j < i + windowSize; j++){ if( (j + searchPattern.length) < filteredSentence.size()){ term = ""; // per ogni tag nel pattern di ricerca for(int k=0; k < searchPattern.length; k++){ // estraggo il termine j+k curr = filteredSentence.get(j+k); // se il tag non corrisponde a quello cercato if(!curr.tag().equalsIgnoreCase(searchPattern[k])){ /* Allora il termine j-esimo non è il primo del pattern * cercato, quindi è un termine che devo considerare da solo */ term = filteredSentence.get(j).word(); break; } // se invece il j+k-esimo termine rispetta il tag lo aggiungo a term term += (curr.word() + " "); /* se sono all'ultima iterazione e term contiene il termine che * rispetta il searchPattern, allora devo spostarmi in avanti di * k termini all'interno della filteredSentence. */ if( k + 1 == searchPattern.length ) j += k; } } else { term = filteredSentence.get(j).word(); } /* Se il termine non è mai stato visto * in questa finestra */ if(!alreadySeen.contains(term)){ // Aggiungo un'occorrenza del termine freq.addOccurrence(term); /* * Aggiungo le occorrenze del termine attuale * con tutti quelli già visti */ for(String w: alreadySeen){ freq.addOccurrence(term, w); } // Aggiungo il termine alla lista dei già visti alreadySeen.add(term); } } } } } } // calcolo le frequenze dividendo per il numero di finestre freq.computeFrequencies(); //freq.filterTerms(lower, upper); return freq; } /** * Metodo che crea i vettori di concetto per ogni termine * @param frequencies Matrice contenente le frequenze di tutti i termini * @param corrFunc Funzione di correlazione da utilizzare * @param alpha Valore soglia per alpha-cut * @return un ContextVectors, oggetto contenente tutti i vettori di contesto */ public static ContextVectors<String> createContextVectors( TermFrequencies<String> frequencies, CorrelationFunction corrFunc, double alpha){ int count; double fc, ft, fct, membership; ArrayList<String> terms = frequencies.getTerms(); ContextVectors<String> contextVectors = new ContextVectors<String>(terms); // Per ogni candidato concetto for(String concept: terms){ count = 0; // prendo la frequenza del concetto candidato fc = frequencies.getFrequency(concept); // Per ogni termine for(String term: terms){ if(concept.equals(term)) continue; /* Recupero la frequenza del termine, e * la frequenza mutua (termine, concetto) */ ft = frequencies.getFrequency(term); fct = frequencies.getFrequency(concept, term); // calcolo la membership, appartenenza di term a concept membership = corrFunc.calculateCorrelation(fc, ft, fct); contextVectors.setMembership(term, concept, membership); } /* se tutti i termini hanno membership minore della soglia * allora concept non è un concetto abbastanza importante * da essere considerato: conservo quest'informazione * ponendo la membership di concept a se stesso a -1 */ } contextVectors.normalizeMemberships(); for(String concept: terms){ count = 0; for(String term: terms){ if ( contextVectors.getMembership(concept, term) < alpha) count++; } if(count == terms.size()-1) contextVectors.deleteConcept(concept); } return contextVectors; } /** * Metodo che costruisce una tassonomia * @param contextVectors Vettori di contesto * @param lambda soglia minima per la relazione fra concetti * @return Un oggetto di tipo Taxonomy<String> */ public static Taxonomy<String> createTaxonomy(ContextVectors<String> contextVectors, double lambda){ // Lista di concetti ArrayList<String> concepts = contextVectors.getConcepts(); // Lista di termini ArrayList<String> terms = contextVectors.getTerms(); // Oggetto tassonomia che verrà restitutito Taxonomy<String> taxonomy = new Taxonomy<String>(concepts); // numeratore e denominatore della formula Spec(ci, cj) double num = 0, denom = 0; // variabili che conterranno la membership di un termine ai due concetti double membership_1, membership_2; // specificità di ci rispetto a cj double spec; // Per ogni coppia di concetti for(String c1: concepts){ for(String c2: concepts){ // se i concetti sono uguali, allora la specificità è 1 if (c1.equals(c2)) taxonomy.setSpecificity(c1, c2, 1); else { // Inizializzo numeratore e denominatore num = 0; denom = 0; // per ogni termine for(String term: terms){ // Calcolo la membership rispetto a c1 e c2 membership_1 = contextVectors.getMembership(c1, term); membership_2 = contextVectors.getMembership(c2, term); // Aggiungo la più piccola delle due al numeratore num += Math.min(membership_1, membership_2); //num += ((membership_1 * membership_2)/(membership_1 + membership_2 - membership_1*membership_2)); // Aggiungo la membership in c1 al denominatore denom += membership_1; } // calcolo la specificità spec = num / denom; // se la specificità è maggiore della soglia if(spec > lambda) // la memorizzo taxonomy.setSpecificity(c1, c2, spec); } } } taxonomy.taxonomyPruning(); return taxonomy; } /** * Metodo che, a partire dal vettore dei concetti e dalle tassonomie, crea l'ontologia * @throws OWLOntologyCreationException * @throws OWLOntologyStorageException * @throws FileNotFoundException **/ public void saveOWL2FuzzyOntology(Taxonomy tax, String fileName) throws OWLOntologyCreationException, OWLOntologyStorageException, FileNotFoundException{ int i,j; OWLClass s,c; //iteratore dei concetti ArrayList<String> concetti=tax.getConcepts(); //gestore dell'ontologia OWLOntologyManager m = OWLManager.createOWLOntologyManager(); //iri di riferimento per l'ontologia IRI ontIRI = IRI.create("http://www.semanticweb.org/ontologies/concetti"); //crea ontologia OWLOntology ont = m.createOntology(ontIRI); //gestore interfaccia per l'ontologia OWLDataFactory factory = m.getOWLDataFactory(); //Per ogni concetto crea una classe nell'ontologia Map<String,OWLClass> classi=new HashMap<String,OWLClass>(); for(String con: concetti){ IRI iriClass = IRI.create(ontIRI + "#" + con); OWLClass cl=factory.getOWLClass(iriClass); classi.put(con, cl); } for(i=0; i<concetti.size(); i++){ for(j=0; j<concetti.size(); j++){ String sub=concetti.get(j); String cls=concetti.get(i); double spec=tax.getSpecificity(sub, cls); if(spec > 0){ OWLAnnotation fuzzyAnnotation = createFuzzyAnnotation(factory, spec); Set<OWLAnnotation> annotazioni = new HashSet<OWLAnnotation>(); annotazioni.add(fuzzyAnnotation); s=classi.get(sub); c=classi.get(cls); OWLAxiom axiom = factory.getOWLSubClassOfAxiom(s, c, annotazioni); AddAxiom add = new AddAxiom(ont, axiom); m.applyChange(add); } } } //salvo l'ontologia su file m.saveOntology(ont, new FileOutputStream(fileName)); } public static OWLAnnotation createFuzzyAnnotation(OWLDataFactory f, double value){ OWLAnnotationProperty fuzzyTag = f.getOWLAnnotationProperty(IRI.create("#fuzzyLabel")); String fuzzyowl2 = "<fuzzyOwl2 fuzzyType=\"axiom\">\n\t<Degree value=\"" + value + "\"/></fuzzyOwl2>"; OWLLiteral fuzzyLiteral = f.getOWLLiteral(fuzzyowl2); return f.getOWLAnnotation(fuzzyTag, fuzzyLiteral); } }